package intermediately

import (
	"encoding/json"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
	"github.com/hyperledger/fabric-sdk-go/pkg/fab/events/endpoint"
	fabmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/prometheus/common/log"
	"strings"
	"time"
)

type Channel struct {
	context *context
	client  *channel.Client
}

func NewChannel() *Channel {
	return &Channel{context: NewContext()}
}

// 1 加载context.json,必须要执行
func (receiver *Channel) LoadContext(contextJsonPath string) *Channel {
	receiver.context.SetContextJson(contextJsonPath)
	return receiver
}

// 2 加载config.yaml,必须要执行
func (receiver *Channel) LoadConfig(configYamlPath string) *Channel {
	receiver.context.SetConfigYaml(configYamlPath)
	return receiver
}

// 3 start Connect
func (receiver *Channel) Connect() (*Channel, error) {
	sdk, err := fabsdk.New(receiver.context.ConfigProvider)
	if err != nil {
		log.Error("! context Error !", err.Error())
		return nil, err
	}
	mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(receiver.context.Org))
	if err != nil {
		log.Error("! New mspclient Error !", err.Error())
		return nil, err
	}
	adminIdentity, err := mspClient.GetSigningIdentity(receiver.context.UserName)
	if err != nil {
		log.Error("! mspClient Admin ERROR !", err.Error())
		return nil, err
	}
	fabsdk.WithIdentity(adminIdentity)
	// 生成通道上下文
	ccp := sdk.ChannelContext(
		receiver.context.ChannelID,
		fabsdk.WithOrg(receiver.context.Org),
		fabsdk.WithUser(receiver.context.UserName),
		fabsdk.WithIdentity(adminIdentity),
	)
	//////////////////////////////////////////
	expectedKeepAliveTime := time.Second
	expectedKeepAliveTimeout := time.Second

	peerConfig := &fab.PeerConfig{
		GRPCOptions: make(map[string]interface{}),
	}
	peerConfig.GRPCOptions["allow-insecure"] = true
	peerConfig.GRPCOptions["fail-fast"] = true
	peerConfig.GRPCOptions["keep-alive-time"] = expectedKeepAliveTime
	peerConfig.GRPCOptions["keep-alive-timeout"] = expectedKeepAliveTimeout
	peerConfig.GRPCOptions["keep-alive-permit"] = true

	config := fabmocks.NewMockEndpointConfig()

	str := strings.Replace(receiver.context.Peer+receiver.context.Org, ".", "", -1)
	peer := fabmocks.NewMockPeer(str, receiver.context.Peer+":7051")

	peerEndpoint := endpoint.FromPeerConfig(config, peer, peerConfig)
	/////////////////////////////////////////

	// 创建 channel client
	channel.WithTargets(peerEndpoint)
	receiver.client, err = channel.New(ccp)

	if err != nil {
		log.Error("! Connect Error !", err.Error())
		return nil, err
	}
	return receiver, nil
}

func (receiver *Channel) GetClient() *channel.Client {
	return receiver.client
}

func (receiver *Channel) Query(chainCodeID, fcn string, args ...string) (channel.Response, error) {
	var defaultQueryArgs [][]byte
	for _, arg := range args {
		defaultQueryArgs = append(defaultQueryArgs, []byte(arg))
	}
	req := channel.Request{
		ChaincodeID: chainCodeID,
		Fcn:         fcn,
		Args:        defaultQueryArgs,
	}
	reqPeers := channel.WithTargetEndpoints(receiver.context.Peer)
	resp, err := receiver.client.Query(req, reqPeers)
	if err != nil {
		return resp, err
	}
	//respJson, err := json.Marshal(resp)
	//if err != nil {
	//	return "", nil
	//}
	return resp, nil
}

func (receiver *Channel) Invoke(chainCodeID, fcn string, args ...string) (channel.Response, error) {
	var defaultQueryArgs [][]byte
	for _, arg := range args {
		defaultQueryArgs = append(defaultQueryArgs, []byte(arg))
	}
	req := channel.Request{
		ChaincodeID: chainCodeID,
		Fcn:         fcn,
		Args:        defaultQueryArgs,
	}
	reqPeers := channel.WithTargetEndpoints(receiver.context.Peer)
	resp, err := receiver.client.Execute(req, reqPeers, channel.WithRetry(retry.DefaultChannelOpts))
	if err != nil {
		return channel.Response{}, err
	}
	//respJson, err := json.Marshal(resp)
	//if err != nil {
	//	return "", nil
	//}
	return resp, nil
}

type EventFunc func(*<-chan *fab.CCEvent)

// 这是链码事件监听函数,
// 用来监听链码事件,比如交易,
// 按照官方写的就是测试不通过，
// 但是我还是把这个功能加上了，
// 自己写闭包，主题函数没有多余的代码
// channel_test.go 有例子
func (receiver *Channel) ChaincodeEvent(chaincodeID, eventID string, eventFunc EventFunc) error {
	reg, notifier, err := receiver.client.RegisterChaincodeEvent(chaincodeID, eventID)
	defer receiver.client.UnregisterChaincodeEvent(reg)
	if err != nil {
		return err
	}
	eventFunc(&notifier)
	return nil
}

// json.RawMessage 类型可以去除 \" 转义字符
type QueryResult struct {
	TransactionID fab.TransactionID
	Payload       json.RawMessage
}
